package com.stellar.redis.core.util;

import cn.hutool.json.JSONUtil;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
public class CacheUtils {

    private static final Random RANDOM = new Random();

    @Resource
    private RedisUtil redisUtil;

    public <T> T getFromCache(String key, Class<T> clazz, Supplier<T> loader, long timeout, TimeUnit timeUnit) {
        String value = redisUtil.get(key);
        if (Strings.isBlank(value)) {
            return loadAndCache(key, clazz, loader, timeout, timeUnit);
        }
        if ("NULL".equals(value)) {
            return null;
        }
        return JSONUtil.toBean(value, clazz);
    }

    private <T> T loadAndCache(String key, Class<T> clazz, Supplier<T> loader, long timeout, TimeUnit timeUnit) {
        String lockKey = key + ":LOCK";
        boolean locked = redisUtil.setNx(lockKey, "LOCKED", 10L, TimeUnit.SECONDS);
        if (locked) {
            try {
                T result = loader.get();
                if (result == null) {
                    redisUtil.setNx(key, "NULL", 1L, TimeUnit.MINUTES);
                } else {
                    long ttl = timeout + RANDOM.nextInt(60); // 随机过期时间
                    redisUtil.setNx(key, JSONUtil.toJsonStr(result), ttl, timeUnit);
                }
                return result;
            } finally {
                redisUtil.del(lockKey);
            }
        } else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return this.getFromCache(key, clazz, loader, timeout, timeUnit);
        }
    }
}